package com.basker.pisces.domain.command.run;

import java.util.ArrayList;
import java.util.List;

import org.springframework.util.CollectionUtils;

import com.basker.pisces.domain.command.event.CommandExecuteEvent;
import com.basker.pisces.domain.command.event.CommandExecuteFailureEvent;
import com.basker.pisces.domain.command.exception.CommandExecuteRuntimeException;
import com.basker.pisces.domain.command.processor.ICommandExecuteProcessor;
import com.basker.pisces.domain.command.processor.ITransactionalCommandExecuteProcessor;

/**
 * 命令执行的事件发布器，每个{@link CommandRunner}都有一个独立的发布器，所以命令的事件是彼此隔离的
 *
 * @author hangwen
 */
class CommandExecuteEventPublisher {
    private List<ICommandExecuteProcessor> processors = null;
    private CommandRunner<?> runner;

    CommandExecuteEventPublisher(CommandRunner<?> runner) {
        this.runner = runner;
    }

    void addProcessor(ICommandExecuteProcessor processor) {
        if (this.processors == null) {
            this.processors = new ArrayList<>();
        }
        this.processors.add(processor);
    }

    void applyProcessAfterInvoke(Object[] dataObjects) {
        CommandExecuteEvent event = this.createEventObject(dataObjects);
        if (event == null) {
            return;
        }

        for (ICommandExecuteProcessor processor : processors) {
            processor.processAfterInvoke(event);
        }
    }

    void applyProcessAfterThrowing(Object[] dataObjects, CommandExecuteRuntimeException exception) {
        CommandExecuteEvent event = this.createEventObject(dataObjects, exception);
        if (event == null) {
            return;
        }

        for (ICommandExecuteProcessor processor : processors) {
            processor.processAfterThrowing((CommandExecuteFailureEvent) event);
        }
    }

    void applyProcessBeforeInvoke(Object[] dataObjects) {
        CommandExecuteEvent event = this.createEventObject(dataObjects);
        if (event == null) {
            return;
        }

        for (ICommandExecuteProcessor processor : processors) {
            processor.processBeforeInvoke(event);
        }
    }

    void applyProcessBeforeTransactionalCommit(Object[] dataObjects) {
        CommandExecuteEvent event = this.createEventObject(dataObjects);
        if (event == null) {
            return;
        }

        for (ICommandExecuteProcessor processor : processors) {
            if (processor instanceof ITransactionalCommandExecuteProcessor) {
                ((ITransactionalCommandExecuteProcessor) processor).processBeforeTransactionalCommit(event);
            }
        }

    }

    void applyProcessBeforeTransactionalInvoke(Object[] dataObjects) {
        CommandExecuteEvent event = this.createEventObject(dataObjects);
        if (event == null) {
            return;
        }

        for (ICommandExecuteProcessor processor : processors) {
            if (processor instanceof ITransactionalCommandExecuteProcessor) {
                ((ITransactionalCommandExecuteProcessor) processor).processBeforeTransactionalInvoke(event);
            }
        }

    }

    void applyProcessCompleteInvoke(Object[] dataObjects) {
        CommandExecuteEvent event = this.createEventObject(dataObjects);
        if (event == null) {
            return;
        }

        for (ICommandExecuteProcessor processor : processors) {
            processor.processCompleteInvoke(event);
        }
    }

    List<ICommandExecuteProcessor> getProcessors() {
        return processors;
    }

    private CommandExecuteEvent createEventObject(Object[] dataObjects) {
        return createEventObject(dataObjects, null);
    }

    private CommandExecuteEvent createEventObject(Object[] dataObjects, CommandExecuteRuntimeException cause) {
        if (CollectionUtils.isEmpty(processors)) {
            return null;
        }

        if (cause == null) {
            return new CommandExecuteEvent(this.runner, dataObjects);
        } else {
            return new CommandExecuteFailureEvent(this.runner, dataObjects, cause);
        }
    }

}
